home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / strlink.com / STRLINK.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1981-09-01  |  27.9 KB  |  865 lines

  1. {$D-,R-,S+}
  2. {$DEFINE TPRO5}
  3.  
  4. UNIT StrLink;
  5.  
  6.  
  7.    {Offers transparent heap-managed linked lists of strings.
  8.  
  9. Version 1.00: released to the public domain on 28 August 1989.
  10.  
  11. Version 1.01: released to the public domain on 1 September 1989.
  12.    Forgot to CLOSE() the files in ReadStrings() and WriteStrings().  They're
  13. fixed now.  ...You know how that got by me?  I testing them by writing strings
  14. to the console.  The buffer always flushes immediately.  But it's a disaster
  15. when you write to a disk file!  I discovered the error without Peter Roach's
  16. help but I thank him anyway for noticing it.
  17.    Fixed an endless loop in Retreat().  The ancestor object doesn't return a
  18. NIL when you try to move backwards from the first string on the list.  So, the
  19. logic had to be installed in Retreat().
  20.    Dan Anderson told me that I forgot to check upper/lower case relevancy in
  21. Exists(), ExistsSubString(), and DeleteDuplicates().  How true, how true.
  22. That's been corrected.
  23.    Added a call to the standard procedure FAIL if the Init() constructor hits
  24. the heap ceiling.  It should never hit the ceiling since we automatically have
  25. been allocated enough room on the heap for ourselves (assuming we're dynamic,
  26. of course).  This is done mostly to show people a little bit about the FAIL
  27. procedure and how it applies to dynamic constructors.  Read the TP 5.5 OOP
  28. guide, p.106-107, for a dissertation on FAIL and heap error recovery.  Also,
  29. read chapter 15 of the ref guide for a way to recover gracefully (!) if the
  30. heap ceiling caves in.  Why go for an abort-203 when you can terminate in a
  31. friendly way?
  32.    Turned the AddString() procedure into a function.  It now returns TRUE or
  33. FALSE based on the success of adding the given string to the heap.  If you get
  34. FALSE, it means you're out of room on the heap.  See above for important
  35. information on heap error recovery.
  36.    Made VIRTUAL methods out of ReadStrings & WriteStrings.
  37.    Changed ReadStrings() & WriteStrings() to return a WORD value, not a BYTE
  38. value.  Peter Roach was kind enough to point me to the TP ref manual where it
  39. explains how IORESULT returns a WORD result.  Also, modified code slightly so
  40. it conditionally turns on/off the $I error checking while reading/writing.
  41.    ReadStrings() now returns a value of MAXINT if it runs out of heap while
  42. it reads a file.
  43.    Added new method, TotalLengthOfStrings, which adds up the string lengths
  44. and returns it as a LONGINT.}
  45.  
  46.  
  47. INTERFACE {section}
  48.  
  49.  
  50. USES
  51.    {$IFDEF TPRO5}
  52.        TpString,
  53.    {$ENDIF}
  54.    Objects,
  55.    ObjectA,
  56.    StrObj;
  57.  
  58. TYPE
  59.    SortedOrderType = (ForwardOrder,
  60.               ReverseOrder,
  61.               AscendingOrder,
  62.               DescendingOrder);
  63.  
  64.    StrLinkList
  65.     = OBJECT(LinkList)
  66.       CurrentStrPtr     : StrObjectPtr;
  67.       UniqueStringsOnly : BOOLEAN;
  68.       SortedOrder       : SortedOrderType;
  69.       CaseMatters       : BOOLEAN;
  70.  
  71.       CONSTRUCTOR Init(UniqueStrings : BOOLEAN;
  72.                        SortSpecifier : SortedOrderType;
  73.                        IgnoreCase    : BOOLEAN);
  74.  
  75.       FUNCTION  GetSpecificString(NodePos : LONGINT) : STRING;
  76.       PROCEDURE DeleteSpecificString(NodePos : LONGINT);
  77.  
  78.       FUNCTION  ReadStrings(TheFilename : STRING) : WORD; VIRTUAL;
  79.       FUNCTION  WriteStrings(TheFilename : STRING;
  80.                              AppendFile  : BOOLEAN) : WORD; VIRTUAL;
  81.  
  82.       FUNCTION  AddString(TheStr : STRING) : BOOLEAN;
  83.       PROCEDURE DeleteString(TheStr : STRING);
  84.       FUNCTION  Exists(TheStr : STRING) : BOOLEAN;
  85.       FUNCTION  ExistsSubstring(TheSubStr : STRING) : BOOLEAN;
  86.       PROCEDURE DeleteStringsWithoutSubstring(TheSubStr  : STRING;
  87.                                               IgnoreCase : BOOLEAN);
  88.       PROCEDURE DeleteStringsWithSubstring(TheSubStr  : STRING;
  89.                                            IgnoreCase : BOOLEAN);
  90.       PROCEDURE DeleteDuplicates;
  91.       PROCEDURE DeleteLeadNullStrings;
  92.       PROCEDURE DeleteNullStrings;
  93.       PROCEDURE DeleteTrailNullStrings;
  94.  
  95.       FUNCTION  TotalLengthOfStrings : LONGINT;
  96.  
  97.       PROCEDURE InitCurrent;
  98.       FUNCTION  CurrentString : STRING;
  99.       PROCEDURE ChangeCurrentString(NewStr : STRING);
  100.       FUNCTION  FirstString : STRING;
  101.       FUNCTION  LastString : STRING;
  102.       PROCEDURE Advance;
  103.       PROCEDURE Retreat;
  104.       FUNCTION  MoreStrings : BOOLEAN;
  105.       FUNCTION  NoMoreStrings : BOOLEAN
  106.       END;
  107.  
  108.  
  109. IMPLEMENTATION {section}
  110.  
  111.  
  112. {$IFNDEF TPRO5}
  113. {============================================================================}
  114. FUNCTION  StUpCase(TheStr : STRING) : STRING;
  115.  
  116.    {Returns a string, converted to uppercase.}
  117.  
  118. VAR
  119.    Index : BYTE;
  120.  
  121. BEGIN {StUpCase}
  122. FOR Index := 1 TO LENGTH(TheStr)
  123.  DO TheStr[Index] := UPCASE(TheStr[Index]);
  124.  
  125. StUpCase := TheStr
  126. END; {StUpCase}
  127. {============================================================================}
  128. {$ENDIF}
  129.  
  130. {- - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - -}
  131.  
  132. {============================================================================}
  133. CONSTRUCTOR StrLinkList.Init(UniqueStrings : BOOLEAN;
  134.                              SortSpecifier : SortedOrderType;
  135.                              IgnoreCase    : BOOLEAN);
  136.  
  137.    {This procedure initializes the StrLinkList.}
  138.  
  139. BEGIN {StrLinkList.Init}
  140. CurrentStrPtr     := NIL;
  141.  
  142. UniqueStringsOnly := UniqueStrings;
  143. SortedOrder       := SortSpecifier;
  144. CaseMatters       := NOT IgnoreCase;
  145.  
  146. IF NOT LinkList.Init
  147.  THEN FAIL
  148. END; {StrLinkList.Init}
  149. {============================================================================}
  150.  
  151. {============================================================================}
  152. FUNCTION  StrLinkList.GetSpecificString(NodePos : LONGINT) : STRING;
  153.  
  154.    {This function returns a string from the StrLinkList based on the position
  155. of a particular Str in the list.  The position is represented by NodePos.  It
  156. returns a null string if NodePos is <= 0 or if it is > Total.  CurrentPtr is
  157. set to the specified string.}
  158.  
  159. BEGIN {StrLinkList.GetSpecificString}
  160. {Initialize.}
  161. CurrentStrPtr := StrObjectPtr(Specific(NodePos));
  162.  
  163. IF (CurrentStrPtr = NIL)
  164.  THEN GetSpecificString := ''
  165.  ELSE GetSpecificString := CurrentStrPtr^.GetString
  166. END; {StrLinkList.GetSpecificString}
  167. {============================================================================}
  168.  
  169. {============================================================================}
  170. PROCEDURE StrLinkList.DeleteSpecificString(NodePos : LONGINT);
  171.  
  172.    {This procedure deletes a string from the StrLinkList based on the position
  173. of the node, represented by NodePos.  It does nothing if NodePos is <= 0 or if
  174. it is > Total.  CurrentPtr is set to NIL afterwards.}
  175.  
  176. BEGIN {StrLinkList.DeleteSpecificString}
  177. {Initialize.}
  178. CurrentStrPtr := StrObjectPtr(Specific(NodePos));
  179.  
  180. IF (CurrentStrPtr <> NIL)
  181.  THEN
  182.     BEGIN
  183.     Remove(CurrentStrPtr);
  184.     DISPOSE(CurrentStrPtr,Done);
  185.     CurrentStrPtr := NIL
  186.     END
  187. END; {StrLinkList.DeleteSpecificString}
  188. {============================================================================}
  189.  
  190. {============================================================================}
  191. FUNCTION  StrLinkList.ReadStrings(TheFilename : STRING) : WORD;
  192.  
  193.    {Reads strings from TheFilename and adds them to the link list.  IORESULT
  194. is returned as the result -- unless we ran out of heap, at which point a value
  195. of MAXINT will be returned.}
  196.  
  197. VAR
  198.    ReadFile : TEXT;
  199.    ReadBuf  : ARRAY [1..2048] OF CHAR;
  200.    ReadLine : STRING;
  201.  
  202.    AddOkay : BOOLEAN;
  203.    IOerror : WORD;
  204.  
  205. BEGIN {StrLinkList.ReadStrings}
  206. {$IFOPT I-}
  207.     {$DEFINE INEG}
  208. {$ELSE}
  209.     {$I-}
  210. {$ENDIF}
  211.  
  212. {Initialize.}
  213. IOerror := IORESULT;
  214. IF (IOerror = 0)
  215.  THEN
  216.     BEGIN
  217.     ASSIGN(ReadFile,TheFilename);
  218.     RESET(ReadFile);
  219.     SETTEXTBUF(ReadFile,ReadBuf);
  220.     IOerror := IORESULT;
  221.     IF (IOerror = 0)
  222.      THEN
  223.         BEGIN
  224.         AddOkay := (IOerror = 0);
  225.  
  226.         WHILE (AddOkay AND NOT EOF(ReadFile) AND (IOerror = 0))
  227.          DO BEGIN
  228.             READLN(ReadFile,ReadLine);
  229.             IOerror := IORESULT;
  230.             AddOkay := AddString(ReadLine)
  231.             END;
  232.  
  233.         {Wrap up.}
  234.         IF (IOerror = 0)
  235.          THEN
  236.             BEGIN
  237.             CLOSE(ReadFile);
  238.             IOerror := IORESULT
  239.             END
  240.         END
  241.     END;
  242.  
  243. IF AddOkay
  244.  THEN ReadStrings := IOerror
  245.  ELSE ReadStrings := MAXINT
  246.  
  247. {$IFDEF INEG}
  248.     {$UNDEF INEG}
  249. {$ELSE}
  250.     {$I+}
  251. {$ENDIF}
  252. END; {StrLinkList.ReadStrings}
  253. {============================================================================}
  254.  
  255. {============================================================================}
  256. FUNCTION  StrLinkList.WriteStrings(TheFilename : STRING;
  257.                                    AppendFile  : BOOLEAN) : WORD;
  258.  
  259.    {Writes strings from TheFilename and adds them to the link list.  IORESULT
  260. is returned as the result.}
  261.  
  262. VAR
  263.    WriteFile : TEXT;
  264.    WriteBuf  : ARRAY [1..2048] OF CHAR;
  265.    WriteLine : STRING;
  266.    IOerror   : WORD;
  267.  
  268. BEGIN {StrLinkList.WriteStrings}
  269. {$IFOPT I-}
  270.     {$DEFINE INEG}
  271. {$ELSE}
  272.     {$I-}
  273. {$ENDIF}
  274.  
  275. {Initialize.}
  276. IOerror := IORESULT;
  277. IF (IOerror = 0)
  278.  THEN
  279.     BEGIN
  280.     ASSIGN(WriteFile,TheFilename);
  281.     IF AppendFile
  282.      THEN SYSTEM.APPEND(WriteFile)
  283.      ELSE REWRITE(WriteFile);
  284.     SETTEXTBUF(WriteFile,WriteBuf);
  285.     IOerror := IORESULT;
  286.  
  287.     WHILE (MoreStrings AND (IOerror = 0))
  288.      DO BEGIN
  289.         WRITELN(WriteFile,CurrentStrPtr^.GetString);
  290.         IOerror := IORESULT;
  291.         Advance
  292.         END;
  293.  
  294.     {Wrap up.}
  295.     IF (IOerror = 0)
  296.      THEN
  297.         BEGIN
  298.         CLOSE(WriteFile);
  299.         IOerror := IORESULT
  300.         END
  301.     END;
  302.  
  303. WriteStrings := IOerror
  304.  
  305. {$IFDEF INEG}
  306.     {$UNDEF INEG}
  307. {$ELSE}
  308.     {$I+}
  309. {$ENDIF}
  310. END; {StrLinkList.WriteStrings}
  311. {============================================================================}
  312.  
  313. {============================================================================}
  314. FUNCTION  StrLinkList.AddString(TheStr : STRING) : BOOLEAN;
  315.  
  316.    {This function stores TheStr in the StrLinkList.  It does nothing if the
  317. string is redundant AND UniqueStringsOnly is set to TRUE.  CurrentPtr is
  318. undefined after making this call.  (It may, or may not, point to the current
  319. string.)
  320.    If this function returns FALSE, it means there was not enough heap to add
  321. the string.}
  322.  
  323. VAR
  324.    TheStrObjPtr : StrObjectPtr;
  325.  
  326. BEGIN {StrLinkList.AddString}
  327. IF (UniqueStringsOnly AND Exists(TheStr))
  328.  THEN
  329.     BEGIN
  330.     AddString := TRUE;
  331.     EXIT {no need to hang around here, eh?}
  332.     END;
  333.  
  334. {Create the string object.}
  335. TheStrObjPtr := NEW(StrObjectPtr,Init(TheStr));
  336. IF (TheStrObjPtr = NIL)
  337.  THEN {we ran out of heap!}
  338.     BEGIN
  339.     AddString := FALSE;
  340.     EXIT {no need to hang around here, eh?}
  341.     END;
  342.  
  343. IF (First = NIL)
  344.  THEN
  345.     Insert(TheStrObjPtr)
  346.  ELSE
  347.     CASE SortedOrder OF
  348.       ForwardOrder :
  349.     Append(TheStrObjPtr);
  350.       ReverseOrder :
  351.     Insert(TheStrObjPtr);
  352.       AscendingOrder :
  353.         BEGIN
  354.         CurrentStrPtr := StrObjectPtr(First);
  355.         IF CaseMatters
  356.          THEN
  357.             WHILE (MoreStrings
  358.              AND (CurrentStrPtr^.GetString < TheStr))
  359.              DO Advance
  360.          ELSE
  361.             {$IFDEF TPRO5}
  362.                 WHILE (MoreStrings
  363.                  AND (CompUCString(CurrentStrPtr^.GetString,TheStr) = Less))
  364.                   DO Advance;
  365.             {$ELSE}
  366.                 WHILE (MoreStrings
  367.                  AND (StUpCase(CurrentStrPtr^.GetString) < StUpCase(TheStr)))
  368.                   DO Advance;
  369.             {$ENDIF}
  370.  
  371.         {CurrentStrPtr now points to the first Str coming after TheStr, or it
  372.            has a NIL value.}
  373.         IF NoMoreStrings
  374.          THEN Append(TheStrObjPtr)
  375.          ELSE Before(TheStrObjPtr,CurrentStrPtr)
  376.         END;
  377.       DescendingOrder :
  378.         BEGIN
  379.         CurrentStrPtr := StrObjectPtr(First);
  380.         IF CaseMatters
  381.          THEN
  382.             WHILE (MoreStrings
  383.              AND (CurrentStrPtr^.GetString > TheStr))
  384.              DO Advance
  385.          ELSE
  386.             {$IFDEF TPRO5}
  387.                 WHILE (MoreStrings
  388.                  AND (CompUCString(CurrentStrPtr^.GetString,
  389.                                    TheStr) = Greater))
  390.                   DO Advance;
  391.             {$ELSE}
  392.                 WHILE (MoreStrings
  393.                  AND (StUpCase(CurrentStrPtr^.GetString) > StUpCase(TheStr)))
  394.                   DO Advance;
  395.             {$ENDIF}
  396.  
  397.         {CurrentStrPtr now points to the first Str coming after TheStr, or it
  398.            has a NIL value.}
  399.         IF NoMoreStrings
  400.          THEN Append(TheStrObjPtr)
  401.          ELSE Before(TheStrObjPtr,CurrentStrPtr)
  402.         END;
  403.      END; {CASE}
  404.  
  405. {If we got this far, everything went okay.}
  406. AddString := TRUE
  407. END; {AddString}
  408. {============================================================================}
  409.  
  410. {============================================================================}
  411. PROCEDURE   StrLinkList.DeleteString(TheStr : STRING);
  412.  
  413.    {This procedure deletes a string from the StrLinkList.  It does nothing if
  414. the string doesn't exist.  CurrentPtr is NIL after making this call.}
  415.  
  416. BEGIN {StrLinkList.DeleteString}
  417. IF Exists(TheStr)
  418.  THEN
  419.     BEGIN
  420.     CurrentStrPtr := StrObjectPtr(First);
  421.     WHILE (CurrentStrPtr^.GetString <> TheStr)
  422.      DO CurrentStrPtr := StrObjectPtr(CurrentStrPtr^.Next);
  423.  
  424.     {CurrentStrPtr now points to the proper string.}
  425.     Remove(CurrentStrPtr);
  426.     DISPOSE(CurrentStrPtr,Done);
  427.     CurrentStrPtr := NIL
  428.     END
  429. END; {StrLinkList.DeleteString}
  430. {============================================================================}
  431.  
  432. {============================================================================}
  433. FUNCTION    StrLinkList.Exists(TheStr : STRING) : BOOLEAN;
  434.  
  435.    {This function determines if the string is on the StrLinkList.}
  436.  
  437. VAR
  438.    TempBoolean : BOOLEAN;
  439.  
  440. BEGIN {StrLinkList.Exists}
  441. {Initialize.}
  442. CurrentStrPtr := StrObjectPtr(First);
  443. IF NOT CaseMatters
  444.  THEN TheStr := StUpCase(TheStr);
  445.  
  446. IF (First = NIL)
  447.  THEN
  448.     Exists := FALSE
  449.  ELSE
  450.     BEGIN
  451.     TempBoolean := FALSE;
  452.  
  453.     IF CaseMatters
  454.      THEN
  455.         REPEAT
  456.             IF (CurrentStrPtr^.GetString = TheStr)
  457.              THEN TempBoolean := TRUE;
  458.              {ELSE leave TempBoolean alone}
  459.  
  460.             CurrentStrPtr := StrObjectPtr(Next(CurrentStrPtr))
  461.          UNTIL (TempBoolean OR NoMoreStrings)
  462.      ELSE
  463.         REPEAT
  464.             {$IFDEF TPRO5}
  465.                 IF (CompUCString(CurrentStrPtr^.GetString,TheStr) = Equal)
  466.                  THEN TempBoolean := TRUE;
  467.                  {ELSE leave TempBoolean alone}
  468.             {$ELSE}
  469.                 IF (StUpCase(CurrentStrPtr^.GetString) = TheStr)
  470.                  THEN TempBoolean := TRUE;
  471.                  {ELSE leave TempBoolean alone}
  472.             {$ENDIF}
  473.  
  474.             CurrentStrPtr := StrObjectPtr(Next(CurrentStrPtr))
  475.          UNTIL (TempBoolean OR NoMoreStrings);
  476.  
  477.     Exists := TempBoolean
  478.     END
  479. END; {StrLinkList.Exists}
  480. {============================================================================}
  481.  
  482. {============================================================================}
  483. FUNCTION    StrLinkList.ExistsSubstring(TheSubStr : STRING) : BOOLEAN;
  484.  
  485.    {This function determines if a given substring is on the StrLinkList.  If
  486. TheSubString is null and at least one string exists on the list, then the
  487. function returns as TRUE.}
  488.  
  489. VAR
  490.    TempBoolean : BOOLEAN;
  491.  
  492. BEGIN {StrLinkList.ExistsSubstring}
  493. {Initialize.}
  494. CurrentStrPtr := StrObjectPtr(First);
  495. IF NOT CaseMatters
  496.  THEN TheSubStr := StUpCase(TheSubStr);
  497.  
  498. IF (First = NIL)
  499.  THEN
  500.     ExistsSubstring := FALSE
  501.  ELSE
  502.     IF (TheSubStr = '')
  503.      THEN
  504.         ExistsSubstring := TRUE
  505.      ELSE
  506.         BEGIN
  507.         TempBoolean := FALSE;
  508.  
  509.         IF CaseMatters
  510.          THEN
  511.             REPEAT
  512.                 IF (POS(TheSubStr,CurrentStrPtr^.GetString) > 0)
  513.                  THEN TempBoolean := TRUE;
  514.                  {ELSE leave TempBoolean alone}
  515.  
  516.                 CurrentStrPtr := StrObjectPtr(Next(CurrentStrPtr))
  517.              UNTIL (TempBoolean OR NoMoreStrings)
  518.          ELSE
  519.             REPEAT
  520.                 IF (POS(TheSubStr,StUpCase(CurrentStrPtr^.GetString)) > 0)
  521.                  THEN TempBoolean := TRUE;
  522.                  {ELSE leave TempBoolean alone}
  523.  
  524.                 CurrentStrPtr := StrObjectPtr(Next(CurrentStrPtr))
  525.              UNTIL (TempBoolean OR NoMoreStrings);
  526.  
  527.         ExistsSubstring := TempBoolean
  528.         END
  529. END; {StrLinkList.ExistsSubstring}
  530. {============================================================================}
  531.  
  532. {============================================================================}
  533. PROCEDURE StrLinkList.DeleteStringsWithoutSubstring(TheSubStr  : STRING;
  534.                                                     IgnoreCase : BOOLEAN);
  535.  
  536.    {This procedure deletes any string that doesn't contain TheSubStr as part
  537. of the string.  No strings are deleted if TheSubString is a null string.  The
  538. IgnoreCase variable dictates whether upper/lower case is relevant.}
  539.  
  540. VAR
  541.    Index : LONGINT;
  542.  
  543. BEGIN {StrLinkList.DeleteStringsWithoutSubstring}
  544. {Initialize.}
  545. IF ((TheSubStr = '') OR (First = NIL))
  546.  THEN EXIT; {no need to hang around, eh?}
  547. InitCurrent;
  548. Index := 1;
  549.  
  550. IF IgnoreCase
  551.  THEN
  552.     BEGIN
  553.     TheSubStr := StUpCase(TheSubStr);
  554.     WHILE (Index <= Total(First))
  555.      DO IF (POS(TheSubStr,StUpCase(GetSpecificString(Index))) = 0)
  556.          THEN DeleteSpecificString(Index)
  557.          ELSE INC(Index)
  558.     END
  559.  ELSE
  560.     WHILE (Index <= Total(First))
  561.      DO IF (POS(TheSubStr,GetSpecificString(Index)) = 0)
  562.          THEN DeleteSpecificString(Index)
  563.          ELSE INC(Index)
  564. END; {StrLinkList.DeleteStringsWithoutSubstring}
  565. {============================================================================}
  566.  
  567. {============================================================================}
  568. PROCEDURE StrLinkList.DeleteStringsWithSubstring(TheSubStr  : STRING;
  569.                                                  IgnoreCase : BOOLEAN);
  570.  
  571.    {This procedure deletes any string that DOES contain TheSubStr as part of
  572. the string.  No strings are deleted if TheSubString is a null string.  The
  573. IgnoreCase variable dictates whether upper/lower case is relevant.}
  574.  
  575. VAR
  576.    Index : LONGINT;
  577.  
  578. BEGIN {StrLinkList.DeleteStringsWithSubstring}
  579. {Initialize.}
  580. IF ((TheSubStr = '') OR (First = NIL))
  581.  THEN EXIT; {no need to hang around, eh?}
  582. InitCurrent;
  583. Index := 1;
  584.  
  585. IF IgnoreCase
  586.  THEN
  587.     BEGIN
  588.     TheSubStr := StUpCase(TheSubStr);
  589.     WHILE (Index <= Total(First))
  590.      DO IF (POS(TheSubStr,StUpCase(GetSpecificString(Index))) > 0)
  591.          THEN DeleteSpecificString(Index)
  592.          ELSE INC(Index)
  593.     END
  594.  ELSE
  595.     WHILE (Index <= Total(First))
  596.      DO IF (POS(TheSubStr,GetSpecificString(Index)) > 0)
  597.          THEN DeleteSpecificString(Index)
  598.          ELSE INC(Index)
  599. END; {StrLinkList.DeleteStringsWithSubstring}
  600. {============================================================================}
  601.  
  602. {============================================================================}
  603. PROCEDURE StrLinkList.DeleteDuplicates;
  604.  
  605.    {This procedure deletes duplicate strings from the list.}
  606.  
  607. VAR
  608.    MasterIndex  : LONGINT;
  609.    CurrentIndex : LONGINT;
  610.    TestStr      : STRING;
  611.  
  612. BEGIN {StrLinkList.DeleteDuplicates}
  613. {Initialize.}
  614. MasterIndex := 1;
  615. InitCurrent;
  616. IF (UniqueStringsOnly OR (Total(First) < 2))
  617.  THEN EXIT; {no need to hang around here, eh?}
  618.  
  619. {If we get this far, we have at least two strings on the list.}
  620. REPEAT
  621.     TestStr       := GetSpecificString(MasterIndex);  {sets CurrentStrPtr}
  622.     CurrentIndex  := SUCC(MasterIndex);
  623.     CurrentStrPtr := StrObjectPtr(Specific(CurrentIndex));
  624.  
  625.     IF CaseMatters
  626.      THEN
  627.         REPEAT
  628.             IF (CurrentStrPtr^.GetString = TestStr)
  629.              THEN
  630.                 BEGIN
  631.                 DeleteSpecificString(CurrentIndex);
  632.                 CurrentStrPtr := StrObjectPtr(Specific(CurrentIndex))
  633.                 END
  634.              ELSE
  635.                 BEGIN
  636.                 Advance;
  637.                 INC(CurrentIndex)
  638.                 END
  639.          UNTIL (CurrentIndex > Total(First))
  640.      ELSE
  641.         {$IFDEF TPRO5}
  642.             REPEAT
  643.                 IF (CompUCString(CurrentStrPtr^.GetString,TestStr) = Equal)
  644.                  THEN
  645.                     BEGIN
  646.                     DeleteSpecificString(CurrentIndex);
  647.                     CurrentStrPtr := StrObjectPtr(Specific(CurrentIndex))
  648.                     END
  649.                  ELSE
  650.                     BEGIN
  651.                     Advance;
  652.                     INC(CurrentIndex)
  653.                     END
  654.              UNTIL (CurrentIndex > Total(First));
  655.         {$ELSE}
  656.             REPEAT
  657.                 IF (StUpCase(CurrentStrPtr^.GetString) = StUpCase(TestStr))
  658.                  THEN
  659.                     BEGIN
  660.                     DeleteSpecificString(CurrentIndex);
  661.                     CurrentStrPtr := StrObjectPtr(Specific(CurrentIndex))
  662.                     END
  663.                  ELSE
  664.                     BEGIN
  665.                     Advance;
  666.                     INC(CurrentIndex)
  667.                     END
  668.              UNTIL (CurrentIndex > Total(First));
  669.         {$ENDIF}
  670.  
  671.     INC(MasterIndex)
  672.  UNTIL (MasterIndex >= Total(First));
  673.  
  674. InitCurrent
  675. END; {StrLinkList.DeleteDuplicates}
  676. {============================================================================}
  677.  
  678. {============================================================================}
  679. PROCEDURE StrLinkList.DeleteLeadNullStrings;
  680.  
  681.    {This procedure deletes leading null strings from the list.  Null strings
  682. that exist past the first non-null string in the list are left alone.}
  683.  
  684. BEGIN {StrLinkList.DeleteLeadNullStrings}
  685. WHILE ((First <> NIL)
  686.  AND (GetSpecificString(1) = ''))
  687.  DO DeleteSpecificString(1)
  688. END; {StrLinkList.DeleteLeadNullStrings}
  689. {============================================================================}
  690.  
  691. {============================================================================}
  692. PROCEDURE StrLinkList.DeleteNullStrings;
  693.  
  694.    {This procedure deletes null strings from the list.}
  695.  
  696. VAR
  697.    Index : LONGINT;
  698.  
  699. BEGIN {StrLinkList.DeleteNullStrings}
  700. {Initialize.}
  701. IF (First = NIL)
  702.  THEN EXIT; {no need to hang around, eh?}
  703. InitCurrent;
  704. Index := 1;
  705.  
  706. WHILE (Index <= Total(First))
  707.  DO IF (GetSpecificString(Index) = '')
  708.      THEN DeleteSpecificString(Index)
  709.      ELSE INC(Index)
  710. END; {StrLinkList.DeleteNullStrings}
  711. {============================================================================}
  712.  
  713. {============================================================================}
  714. PROCEDURE StrLinkList.DeleteTrailNullStrings;
  715.  
  716.    {This procedure deletes Trailing null strings from the list.  Null strings
  717. that exist before the last non-null string in the list are left alone.}
  718.  
  719. BEGIN {StrLinkList.DeleteTrailNullStrings}
  720. WHILE ((Last <> NIL)
  721.  AND (GetSpecificString(Total(First)) = ''))
  722.  DO DeleteSpecificString(Total(First))
  723. END; {StrLinkList.DeleteTrailNullStrings}
  724. {============================================================================}
  725.  
  726. {============================================================================}
  727. FUNCTION  StrLinkList.TotalLengthOfStrings : LONGINT;
  728.  
  729.    {Returns the total length of the strings on the list.  CurrentStrPtr points
  730. to NO string after the call is made.}
  731.  
  732. VAR
  733.    Index : LONGINT;
  734.  
  735. BEGIN {StrLinkList.TotalLengthOfStrings}
  736. {Initialize.}
  737. Index := 0;
  738. InitCurrent;
  739.  
  740. WHILE MoreStrings
  741.  DO BEGIN
  742.     INC(Index,CurrentStrPtr^.GetStringLength);
  743.     Advance
  744.     END;
  745.  
  746. TotalLengthOfStrings := Index
  747. END; {StrLinkList.TotalLengthOfStrings}
  748. {============================================================================}
  749.  
  750. {============================================================================}
  751. PROCEDURE  StrLinkList.InitCurrent;
  752.  
  753.    {This function initializes CurrentStrPtr to point to the first string on
  754. the LinkList.  NoMoreStrings will return TRUE if there are no strings on the
  755. list.}
  756.  
  757. BEGIN {StrLinkList.InitCurrent}
  758. CurrentStrPtr := StrObjectPtr(First);
  759. END; {StrLinkList.InitCurrent}
  760. {============================================================================}
  761.  
  762. {============================================================================}
  763. FUNCTION    StrLinkList.CurrentString : STRING;
  764.  
  765.    {This function returns the current string in the StrLinkList.  It returns
  766. a null string if the CurrentStrPtr is NIL.  It is up to the calling routine
  767. to use the NoMoreStrings function to see if a string is currently available.}
  768.  
  769. BEGIN {StrLinkList.CurrentString}
  770. IF NoMoreStrings
  771.  THEN CurrentString := ''
  772.  ELSE CurrentString := CurrentStrPtr^.GetString
  773. END; {StrLinkList.CurrentString}
  774. {============================================================================}
  775.  
  776. {============================================================================}
  777. PROCEDURE StrLinkList.ChangeCurrentString(NewStr : STRING);
  778.  
  779.    {This procedure changes the current string to the new string.}
  780.  
  781. BEGIN {StrLinkList.ChangeCurrentString}
  782. CurrentStrPtr^.ChangeString(NewStr)
  783. END; {StrLinkList.ChangeCurrentString}
  784. {============================================================================}
  785.  
  786. {============================================================================}
  787. FUNCTION    StrLinkList.FirstString : STRING;
  788.  
  789.    {This function simply returns the first String in the LinkList.  It returns
  790. a null string if there are no strings in the list.  It is up to the calling
  791. routine to determine for itself if there are no strings.}
  792.  
  793. BEGIN {StrLinkList.FirstString}
  794. CurrentStrPtr := StrObjectPtr(First);
  795. IF NoMoreStrings
  796.  THEN FirstString := ''
  797.  ELSE FirstString := CurrentStrPtr^.GetString
  798. END; {StrLinkList.FirstString}
  799. {============================================================================}
  800.  
  801. {============================================================================}
  802. FUNCTION    StrLinkList.LastString : STRING;
  803.  
  804.    {This function simply returns the last string in the LinkList.  It returns
  805. a null string if there are no strings in the list.  It is up to the calling
  806. routine to determine for itself if there are no strings.}
  807.  
  808. BEGIN {StrLinkList.LastString}
  809. CurrentStrPtr := StrObjectPtr(Last);
  810. IF NoMoreStrings
  811.  THEN LastString := ''
  812.  ELSE LastString := CurrentStrPtr^.GetString
  813. END; {StrLinkList.LastString}
  814. {============================================================================}
  815.  
  816. {============================================================================}
  817. PROCEDURE   StrLinkList.Advance;
  818.  
  819.    {This procedure simply moves to the next string in the StrLinkList.}
  820.  
  821. BEGIN {StrLinkList.Advance}
  822. CurrentStrPtr := StrObjectPtr(Next(CurrentStrPtr))
  823. END; {StrLinkList.Advance}
  824. {============================================================================}
  825.  
  826. {============================================================================}
  827. PROCEDURE   StrLinkList.Retreat;
  828.  
  829.    {This procedure simply moves to the previous string in the StrLinkList.
  830. Use the MoreStrings or NoMoreStrings functions to determine if CurrentString
  831. points to a valid string after you make this call.  Note:  Retreat will not
  832. retreat past the first string on the list.}
  833.  
  834. BEGIN {StrLinkList.Retreat}
  835. IF CurrentStrPtr = StrObjectPtr(First)
  836.  THEN CurrentStrPtr := NIL
  837.  ELSE CurrentStrPtr := StrObjectPtr(Prev(CurrentStrPtr))
  838. END; {StrLinkList.Retreat}
  839. {============================================================================}
  840.  
  841. {============================================================================}
  842. FUNCTION  StrLinkList.MoreStrings : BOOLEAN;
  843.  
  844.    {This function tells the calling routine if there are still some strings
  845. left to go on the link list.}
  846.  
  847. BEGIN {StrLinkList.MoreStrings}
  848. MoreStrings := (CurrentStrPtr <> NIL)
  849. END; {StrLinkList.MoreStrings}
  850. {============================================================================}
  851.  
  852. {============================================================================}
  853. FUNCTION  StrLinkList.NoMoreStrings : BOOLEAN;
  854.  
  855.    {This function is just the opposite of MoreStrings.  It tells the calling
  856. routine if the string link list has been exhausted.}
  857.  
  858. BEGIN {StrLinkList.NoMoreStrings}
  859. NoMoreStrings := (CurrentStrPtr = NIL)
  860. END; {StrLinkList.NoMoreStrings}
  861. {============================================================================}
  862.  
  863.  
  864. END. {StrLink}
  865.